"""
AdaGrad

AdaGrad算法会在单个坐标层面动态降低学习率
AdaGrad算法利用梯度的大小作为调整进度速率的手段：用较小的学习率弥补较大梯度的坐标
在深度学习问题中，由于内存和计算限制，计算准确的二阶导数通常是不可行的。梯度可以作为一个有效的代理
如果优化问题的结构相当不均匀，AdaGrad算法可以帮助环节扭曲
AdaGrad算法对于稀疏特征特别有效，在此情况下由于不常出现的问题，学习率需要更慢地下降
在深度学习问题上，AdaGrad算法有时在降低学习率方面可能过于剧烈
"""
import math

import numpy as np
import matplotlib.pyplot as plt

w1 = np.linspace(-1, 1, 30)
w2 = np.linspace(-0.8, 0.8, 30)
e = 0.1 * w1 ** 2 + 2 * w2 ** 2

current_w1 = -1
current_w2 = -0.8
# 当提高学习率为0.8时，学习梯度会下降的更加猛烈
lr = 0.2
num_epochs = 10
predict_w = []

# 防零小偏置项
eps = 1e-6
# 累加梯度
s1 = 0
s2 = 0

for i in range(num_epochs):
    predict_w.append([current_w1, current_w2])
    slope1 = 2 * current_w1
    slope2 = 4 * current_w2
    s1 += slope1 ** 2
    s2 += slope2 ** 2
    current_w1 -= lr / math.sqrt(s1 + eps) * slope1
    current_w2 -= lr / math.sqrt(s2 + eps) * slope2

predict_w = np.array(predict_w)
x1, x2 = np.meshgrid(w1, w2, indexing='ij')
plt.contour(x1, x2, x1 ** 2 + 2 * x2 ** 2, colors="#1f77b4")
plt.plot(predict_w[:, 0], predict_w[:, 1], 'ro-')
plt.show()
